/**
 * Copyright 2004 Cordys R&D B.V. 
 * 
 * This file is part of the Cordys FTP Connector. 
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 * 
 * http://www.apache.org/licenses/LICENSE-2.0
 * 
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
 package com.eibus.applicationconnector.ftp;

import java.util.Iterator;
import java.util.List;
import java.util.Vector;

import javax.xml.namespace.QName;

import com.eibus.xml.nom.Node;
import com.eibus.xml.xpath.XPath;
import com.eibus.soap.BodyBlock;
import com.eibus.soap.Processor;
import com.eibus.util.Base64;
import com.eibus.util.ConnectorUtils;
import com.eibus.util.logger.Severity;
import com.eibus.soap.SOAPTransaction;
import com.eibus.util.logger.CordysLogger;
import com.eibus.soap.ApplicationTransaction;
import com.eibus.soap.fault.Fault;
import com.eibus.applicationconnector.ftp.FTPCommand;
import com.eibus.applicationconnector.ftp.localization.message.Messages;

public class FTPTransaction implements ApplicationTransaction 
{
	private final Processor processor;
	private List<FTPCommand> transactionalObjects;
	private static final CordysLogger logger = CordysLogger.getCordysLogger(FTPTransaction.class); 
	public FTPConfiguration ftpConfiguration = new FTPConfiguration();
	private static final String XMLSTORE_KEY = "/Cordys/WCP/FTP/ftpregistry";

    protected static final  String DEFAULT_NAME_SPACE = "http://schemas.cordys.com/1.0/ftp";
    protected static final QName DEFAULT_QNAME = new QName(DEFAULT_NAME_SPACE,"customFaultDetail");
	
	public FTPTransaction(Processor processor, SOAPTransaction soapTransaction)
	{
		this.processor = processor;
	}

	@Override
    public void abort()
    {
        if (transactionalObjects==null)
        {
        	return;        
        }
        
        FTPCommand ftpCommand = null;
        
        Iterator<FTPCommand> iterator = transactionalObjects.iterator();
        while (iterator.hasNext())
        {
        	ftpCommand = iterator.next();
        	ftpCommand.disconnect();
        }
    }
	
    private void add(com.eibus.applicationconnector.ftp.FTPCommand commandHandler)
    {
        if(transactionalObjects==null)
        {
			transactionalObjects = new Vector<FTPCommand>();
		}
        transactionalObjects.add(commandHandler);
    }
    
	@Override
	public boolean canProcess(String implementationType)
	{
		return ("FTP".equals(implementationType));
	}

	@Override
	public void commit()
	{
        if(transactionalObjects==null)
        {
        	return;
        }
        
        FTPCommand ftpCommand = null;
        
        Iterator<FTPCommand> iterator = transactionalObjects.iterator();
        while (iterator.hasNext())
        {
        	ftpCommand = iterator.next();
        	ftpCommand.commit();
        	
            if (logger.isInfoEnabled())
            {
            	logger.log(Severity.INFO, "Response from  ftp service is: "+Node.writeToString(ftpCommand.response.getXMLNode(),true));
            }
        }
	}

	@Override
	public boolean process(BodyBlock request, BodyBlock response)
	{
		int requestNode = request.getXMLNode();
		
		String command = Node.getLocalName(requestNode);
		
		/* 
		 * Call the factory method getHandler() which returns the 
		 * appropriate handler based on the command passed to it.
		 */
		FTPCommand ftpCommandHandler = CommandFactory.getHandler(command);
		
		ftpCommandHandler.setResponse(response);
		ftpCommandHandler.setProcessor(processor);
		ftpCommandHandler.setRequestXML(request.getXMLNode());
		ftpCommandHandler.setFtpConfiguration(ftpConfiguration);
		ftpCommandHandler.setOrgContext(processor.getOrganization());
		FTPCommand.METHOD_NAMESPACE = ftpCommandHandler.getNameSpace();
		ftpCommandHandler.setFtpNotificationSubject(Node.getDataWithDefault(XPath.getFirstMatch("notification-subject", null, requestNode), ""));
		
		add(ftpCommandHandler);
		
		boolean isSuccess = initializeFTPSettings(request, response);
		if (!isSuccess)
		{
			return false;
		}
		
		boolean isConnected = ftpCommandHandler.connect();
		if (!isConnected)
		{	
			return false;
		}
		
		return (ftpCommandHandler.prepare());
	}

	/**
	 * This method initializes the instance of FTPConfiguration
	 * The same object is initialized from the requests coming
	 * from both C3 and BOP-4.
	 *  
	 * @param request
	 * @param response
	 * @return true/false
	 */
	private boolean initializeFTPSettings(BodyBlock request, BodyBlock response)
	{
		int requestNode = request.getXMLNode();
		
		int configurationNode = XPath.getFirstMatch("configuration", null, requestNode);
		
		if (configurationNode == 0)	// C3 support
		{
			int serverNode = XPath.getFirstMatch("server", null, requestNode);
			ftpConfiguration.setServer(Node.getDataWithDefault(serverNode, ""));
			
			int userNode = XPath.getFirstMatch("user", null, requestNode);
			ftpConfiguration.setUsername(Node.getDataWithDefault(userNode, ""));
			
			int passwordNode = XPath.getFirstMatch("password", null, requestNode);
			ftpConfiguration.setPassword(Node.getDataWithDefault(passwordNode, ""));
			
			int passiveNode = XPath.getFirstMatch("passive", null, requestNode);
			if (Node.getDataWithDefault(passiveNode, "").equals("true"))
			{
				ftpConfiguration.setActiveMode(false);
			}
			ftpConfiguration.setProtocol("FTP");
			
			int portNode = XPath.getFirstMatch("portno", null, requestNode);
			try
			{
				ftpConfiguration.setPort(Integer.parseInt(Node.getDataWithDefault(portNode, "")));
			}
			catch (NumberFormatException nfe)
			{
				response.createSOAPFault(Fault.Codes.CLIENT, Messages.INVALID_PORT_SPECIFIED);
				return false;
			}
		}
		else	// BOP-4 support
		{
			int ftpConfigNode = ConnectorUtils.getFtpConfiguration(Node.getDataWithDefault(configurationNode, ""), 
					request.getSOAPTransaction().getIdentity().getOrgUserDN(), 
					request.getSOAPTransaction().getIdentity().getUserOrganization(), 
					"organization", XMLSTORE_KEY);
			try
			{
				if (ftpConfigNode == 0)
				{
					logger.log(Severity.ERROR, "Configuration not found in XMLStore with the name " + Node.getDataWithDefault(configurationNode, ""));
					response.createSOAPFault(Fault.Codes.CLIENT, Messages.CONFIGURATION_NOT_FOUND, new String[]{Node.getDataWithDefault(configurationNode, "")});
					return false;
				}
				
				int serverNode = XPath.getFirstMatch("data/server", null, ftpConfigNode);
				ftpConfiguration.setServer(Node.getDataWithDefault(serverNode, ""));
				
				int userNode = XPath.getFirstMatch("data/username", null, ftpConfigNode);
				ftpConfiguration.setUsername(Node.getDataWithDefault(userNode, ""));
				
				int passwordNode = XPath.getFirstMatch("data/password", null, ftpConfigNode);
				ftpConfiguration.setPassword(Base64.decode(Node.getDataWithDefault(passwordNode, "")));
				
				int portNode = XPath.getFirstMatch("data/port", null, ftpConfigNode);
				try
				{
					ftpConfiguration.setPort(Integer.parseInt(Node.getDataWithDefault(portNode, "")));
				}
				catch (NumberFormatException nfe)
				{
					response.createSOAPFault(Fault.Codes.CLIENT, Messages.INVALID_PORT_SPECIFIED);
					return false;
				}
				
				int protocolNode = XPath.getFirstMatch("data/protocol", null, ftpConfigNode);
				ftpConfiguration.setProtocol(Node.getDataWithDefault(protocolNode, ""));
	
				/* Set the transfer mode: ACTIVE or PASSIVE */	
				int transferModeNode = XPath.getFirstMatch("data/transfermode", null, ftpConfigNode);
				if (Node.getDataWithDefault(transferModeNode, "").equals("ACTIVE"))
				{
					ftpConfiguration.setActiveMode(true);
				}
				else
				{
					ftpConfiguration.setActiveMode(false);
				}
				
				/* Check if the Proxy is set in soap processor configuration */
				int processorConfigurationNode = FTPHandler.configurationNode;
				int proxySetNode = XPath.getFirstMatch("ftpProxySet", null, processorConfigurationNode);
				if (Node.getDataWithDefault(proxySetNode, "false").equals("true"))
				{
					ftpConfiguration.setProxyEnabled(true);
					int proxyTypeNode = XPath.getFirstMatch("ftpProxyType", null, processorConfigurationNode);
					int proxyHostNode = XPath.getFirstMatch("ftpProxyHost", null, processorConfigurationNode);
					int proxyPortNode = XPath.getFirstMatch("ftpProxyPort", null, processorConfigurationNode);
					int proxyUserNameNode = XPath.getFirstMatch("ftpProxyUsername", null, processorConfigurationNode);
					int proxyPasswordNode = XPath.getFirstMatch("ftpProxyPassword", null, processorConfigurationNode);
					
					ProxySettings proxySettings = ftpConfiguration.getProxySettings();
					proxySettings.setProxyType(Node.getDataWithDefault(proxyTypeNode, ""));
					proxySettings.setProxyHost(Node.getDataWithDefault(proxyHostNode, ""));
					proxySettings.setProxyUsername(Node.getDataWithDefault(proxyUserNameNode, ""));
					proxySettings.setProxyPassword(Base64.decode(Node.getDataWithDefault(proxyPasswordNode, "")));
					
					try
					{
						proxySettings.setProxyPort(Integer.parseInt(Node.getDataWithDefault(proxyPortNode, "")));
					}
					catch (NumberFormatException nfe)
					{
						response.createSOAPFault(Fault.Codes.CLIENT, Messages.INVALID_PORT_SPECIFIED);
						return false;
					}
				}
				else
				{
					ftpConfiguration.setProxyEnabled(false);
				}
				
				int basepathNode = XPath.getFirstMatch("data/basepath", null, ftpConfigNode);
				ftpConfiguration.setBasepath(Node.getDataWithDefault(basepathNode, ""));
			} finally
			{
				Node.delete(ftpConfigNode);
				ftpConfigNode = 0;
			}
		}
		
		ftpConfiguration.setDocument(Node.getDocument(requestNode));
		return true;
	}
}